from collections import deque
from comment.modules.debt_info import Debt_info
from comment.modules.matched_result import Matched_result
from comment.modules.expected_return import Expected_return
from comment.modules.funding_not_matched import Funding_not_matched
from comment.utils.gen_trans_id import gen_trans_id
from financial.resources.transaction.const import DealType
from sqlalchemy import or_
from flask import current_app
from comment.modules import db
from datetime import datetime
from dateutil.relativedelta import relativedelta
from flask_restful import Resource

class MatchQueue:
    """
    采用deque来构建一个栈。禁用deque的右端，只使用左端
    """

    def __init__(self, item_list):
        """ 初始化栈 """
        self.items = deque(item_list)

    #  从栈中去一条待匹配的数据
    def get_item(self):
        return self.items.popleft()

    #  将一条即将待匹配的数据 放入 栈中
    def push(self, item):
        return self.items.appendleft(item)

    #  判断栈中的数据是否为空
    @property
    def empty(self):
        return len(self.items) == 0


class Match(object):
    """
    正式开始撮合匹配资金
    """

    def __init__(self):
        """初始化，把所有待匹配的债权和待匹配的资金都从数据库取出来"""
        # 查询未匹配或者部分匹配的债权列表
        debt_list = Debt_info.query.filter(or_(Debt_info.matchedStatus == 0, Debt_info.matchedStatus == 1)).order_by(
            Debt_info.id).all()
        self.debtDeque = MatchQueue(debt_list)  # 待匹配的债权 栈

        # 未匹配或部分匹配资金列表
        fund_list = Funding_not_matched.query.filter( \
            or_(Funding_not_matched.matchedStatus == 0, Funding_not_matched.matchedStatus == 1)).order_by(
            Funding_not_matched.fId).all()

        self.fundsDeque = MatchQueue(fund_list)

    def start_match(self):
        """正式开始撮合匹配"""
        try:
            while (not self.fundsDeque.empty) and (not self.debtDeque.empty):
                debt_item = self.debtDeque.get_item()
                fund_item = self.fundsDeque.get_item()
            # 匹配有三种情况，第一：债权资金金额 大于 理财金额 , 第二种：债权资金金额 小于 理财金额 ；第三： 债权资金金额 等于 理财金额
                if debt_item.debtMoney > fund_item.fNotMatchedMoney:
                    self.debtMoneyMoreThanFunds(debt_item, fund_item)
                elif debt_item.debtMoney < fund_item.fNotMatchedMoney:
                    self.debtMoneyLessThanFunds(debt_item, fund_item)
                else:
                    self.debtMoneyEqualFunds(debt_item, fund_item)

            db.session.commit()
            return {'msg': '匹配完成'}
        except Exception as e:
            current_app.logger.error(e)
            db.session.rollback()
            return {'message': '服务器忙, 请稍后重试'}, 500

    #  债权资金部分匹配的业务逻辑: 第一：债权资金金额 大于 理财金额
    def debtMoneyMoreThanFunds(self, debt, funds):
        #  新增一条匹配结果
        self.createMatchedResult(debt, funds, funds.fNotMatchedMoney)
        debt.debtMoney -= funds.fNotMatchedMoney  # 债权剩下未匹配的金额
        debt.matchedStatus = 1  # 修改债权的匹配状态为1
        debt.matchedMoney += funds.fNotMatchedMoney  # 修改债权的已经匹配金额

        funds.fNotMatchedMoney = 0
        funds.matchedStatus = 2  # 修改债权的匹配状态为2
        # 债权还剩下一点资金没有匹配， 马上重新插入到栈
        self.debtDeque.push(debt)
        # 投资理财的金额全部匹配完了，所以可以开始产生收益
        self.createExpectedResult(funds)

    def debtMoneyLessThanFunds(self, debt, funds):
        """带匹配的债权资金 小于 投资理财的资金"""
        funds.fNotMatchedMoney -= debt.debtMoney  # 投资资金剩余未匹配金额
        funds.matchedStatus = 1
        self.createMatchedResult(debt, funds, debt.debtMoney)
        debt.matchedMoney += debt.debtMoney  # 修改债权中已经匹配的金额
        debt.debtMoney = 0  # 匹配完成之后，修改债权的待匹配金额
        debt.user.accountInfo.balance += debt.repaymenMoney
        debt.matchedStatus = 2
        self.startRepayPlay(debt)  # 正式开始还款计划
        self.fundsDeque.push(funds)  # 把剩余没有匹配的资金放入栈中

    # 第三种： 投资资金 等于 债权的资金
    def debtMoneyEqualFunds(self, debt, funds):
        debt.matchedStatus = 2  # 债权匹配状态 0未匹配 1部分 2完全匹配
        debt.matchedMoney += debt.debtMoney  # 修改债权中已经匹配的金额
        self.createMatchedResult(debt, funds, debt.debtMoney)
        debt.debtMoney = 0  # 债权待匹配的金额
        funds.fNotMatchedMoney = 0
        funds.matchedStatus = 2
        self.createExpectedResult(funds)
        self.startRepayPlay(debt)


    def startRepayPlay(self,debt):

        repay_list = debt.debtor_record
        for i in range(len(repay_list)):
            repay_date = datetime.now() + relativedelta(months=(i+1))
            # 修改还款计划中还款时间
            repay_list[i].receivableDate = repay_date



    def createMatchedResult(self, debt, funds, match_money):
        """创建一条匹配结果数据，并保存到数据库中"""
        match_num = gen_trans_id(DealType.match.name)
        result = Matched_result(userId=funds.investRecord.pUid, debtId=debt.id, investId=funds.investRecord.pId,
                                transferSerialNo=match_num, purchaseMoney=match_money,
                                confirmedDate=funds.investRecord.pDate,
                                isConfirmed=1, matchDate=datetime.now())

        db.session.add(result)

    def createExpectedResult(self, funds):
        """创建一条预期收益的记录"""
        ex_return = Expected_return(userId=funds.investRecord.pUid, productId=funds.investRecord.pProductId,
                                    investRecord=funds.investRecord.pId,
                                    expectedDate=datetime.now() + relativedelta(months=funds.investRecord.pDeadline),
                                    expectedMoney=funds.investRecord.pProspectiveEarnings)
        funds.investRecord.pStatus = 1
        # 修改 投资记录的开始计息时间
        funds.investRecord.pInterestStartDate = datetime.now()
        db.session.add(ex_return)


class MatchUp(Resource):

    def post(self):
        match = Match()
        return match.start_match()